import { Component, Host, h, VNode, Prop, ComponentInterface, State, Watch, forceUpdate, Element, Listen } from '@stencil/core';
import { Subscription } from 'rxjs';
import { Moment } from 'moment';
import { IBzCalendarWeekController } from '../core/controller/ibz-calendar-week-controller';
import { IBzCalendarWeekNode } from '../core/interface/ibz-calendar-week-node';
import { CalendarEvent } from '../core/calendar-global';
import { calendarUtil } from '@/utils/calendar-util/calendar-util';

/**
 * 「天」数据呈现
 *
 * @export
 * @class IBzWeekDay
 */
@Component({
    tag: 'ibz-week-day',
    styleUrl: 'ibz-week-day.scss'
})
export class IBzWeekDay implements ComponentInterface {

    /**
     * 组件DOM实例
     *
     * @type {HTMLElement}
     * @memberof IBzWeekDay
     */
    @Element()
    public el: HTMLElement;

    /**
     * 日历「周」模式控制器
     *
     * @type {IBzCalendarWeekController}
     * @memberof IBzWeekDay
     */
    @Prop()
    public $c: IBzCalendarWeekController;

    /**
     * 天数在当前日历的下标
     *
     * @type {number}
     * @memberof IBzWeekDay
     */
    @Prop()
    public index: number;

    /**
     * 此值变化时，重新计算今日数据
     *
     * @type {number}
     * @memberof IBzWeekDay
     */
    @Prop()
    public recalculate: number = 0;

    /**
     * 监控「recalculate」值变化
     *
     * @param {number} val
     * @param {number} oldVal
     * @memberof IBzWeekDay
     */
    @Watch('recalculate')
    public watchRecalculate(val: number, oldVal: number): void {
        if (val !== oldVal) {
            this.calcItems();
        }
    }

    /**
     * 是否悬浮在当前组件之上
     *
     * @protected
     * @type {boolean}
     * @memberof IBzWeekDay
     */
    // @State()
    protected isHover: boolean = false;

    /**
     * 今日起始时间
     *
     * @protected
     * @type {Moment}
     * @memberof IBzWeekDayItem
     */
    protected todayStart: Moment;

    /**
     * 今日结束时间
     *
     * @protected
     * @type {Moment}
     * @memberof IBzWeekDayItem
     */
    protected todayEnd: Moment;

    /**
     * 今日时间标识
     *
     * @protected
     * @type {string}
     * @memberof IBzWeekDay
     */
    @State()
    protected todayKey: string = '';

    /**
     * 订阅鼠标进入
     *
     * @memberof IBzWeekDay
     */
    @Listen('mouseenter')
    public onmouseenter(): void {
        this.isHover = true;
    }

    /**
     * 订阅鼠标离开
     *
     * @memberof IBzWeekDay
     */
    @Listen('mouseleave')
    public onmouseleave(): void {
        this.isHover = false;
    }

    /**
     * 日期
     *
     * @type {string}
     * @memberof IBzWeekDay
     */
    @Prop()
    public date: string;

    /**
     * 监控日期变更
     *
     * @memberof IBzWeekDay
     */
    @Watch('date')
    public watchDate(): void {
        this.calcTodayDate();
    }

    /**
     * 当前日期数据
     *
     * @protected
     * @type {IBzCalendarWeekNode[]}
     * @memberof IBzWeekDay
     */
    @State()
    protected items: IBzCalendarWeekNode[] = [];

    /**
     * 数据绘制层级缓存
     *
     * @protected
     * @type {Map<string | number, { level: number, data: IBzCalendarWeekNode }>}
     * @memberof IBzWeekDay
     */
    protected dataLevelMap: Map<string | number, { level: number, data: IBzCalendarWeekNode }> = new Map();

    /**
     * 已订阅的Rxjs
     *
     * @protected
     * @type {Subscription[]}
     * @memberof IBzWeekDay
     */
    protected subs: Subscription[] = [];

    /**
     * 组件加载之前
     *
     * @memberof IBzWeekDay
     */
    public componentWillLoad(): void {
        this.calcTodayDate();
        this.initControllerEvent();
        this.calcItems();
    }

    /**
     * 组件取消DOM连接
     *
     * @memberof IBzWeekDay
     */
    public disconnectedCallback(): void {
        if (!this.el.isConnected) {
            this.subs.forEach((item) => {
                item.unsubscribe();
            });
        }
    }

    /**
     * 订阅控制器事件
     *
     * @protected
     * @memberof IBzWeekDay
     */
    protected initControllerEvent(): void {
        this.subs.push(this.$c.changeDayItemsObservable().subscribe((m: Moment) => {
            if (this.todayStart.isSame(m)) {
                this.calcItems();
            }
        }));
        this.subs.push(this.$c.on(CalendarEvent.OptionChange).subscribe(() => {
            this.calcTodayDate();
        }));
    }

    /**
     * 计算今日，起始和结束时间
     *
     * @protected
     * @memberof IBzWeekDay
     */
    protected calcTodayDate(): void {
        this.todayStart = this.$c.calcDayStartDate(this.date);
        this.todayEnd = this.$c.calcDayEndDate(this.date);
        this.todayKey = this.$c.calcDayKey(this.todayStart, this.todayEnd);
    }

    /**
     * 计算今日数据
     *
     * @protected
     * @memberof IBzWeekDay
     */
    protected calcItems(): void {
        this.items = this.$c.getSpecifyDateItems(this.date);
        forceUpdate(this);
        this.calcItemLevel();
    }

    /**
     * 计算项绘制层级
     *
     * @protected
     * @param {number} [level=0]
     * @memberof IBzWeekDay
     */
    protected calcItemLevel(level: number = 0) {
        this.dataLevelMap.clear();
        if (this.items.length > 0) {
            let last: IBzCalendarWeekNode = this.items[0];
            this.dataLevelMap.set(last.id, { level, data: last });
            const items = this.items.filter((item) => {
                if (this.dataLevelMap.has(item.id)) {
                    return;
                }
                if (!calendarUtil.isOverlapsTime(last.startMoment, last.endMoment, item.startMoment, item.endMoment)) {
                    this.dataLevelMap.set(item.id, { level, data: item });
                    last = item;
                    return true;
                }
            });
            items.splice(0, 1, last);
            for (let i = 0; i < 10; i++) {
                let over = true;
                this.dataLevelMap.forEach((item) => {
                    if (item.level === i) {
                        const items = this.calcItemLevel2(item.data, i + 1);
                        if (over) {
                            over = !(items.length > 0);
                        }
                    }
                });
                if (over) {
                    break;
                }
            }
        }
    }

    /**
     * 根据父计算项层级，只计算2层及以后
     *
     * @protected
     * @param {IBzCalendarWeekNode} p
     * @param {number} level
     * @returns {IBzCalendarWeekNode[]}
     * @memberof IBzWeekDay
     */
    protected calcItemLevel2(p: IBzCalendarWeekNode, level: number): IBzCalendarWeekNode[] {
        let last: IBzCalendarWeekNode;
        return this.items.filter((item) => {
            if (this.dataLevelMap.has(item.id)) {
                return;
            }
            // 时间是否重叠
            let isRepeat = false;
            this.dataLevelMap.forEach((self) => {
                if (self.level === level && self.data.startMoment.isSame(item.startMoment)) {
                    isRepeat = true;
                }
            });
            if (isRepeat) {
                return true;
            }
            if ((item.startMoment.isBetween(p.startMoment, p.endMoment) || p.startMoment.isSame(item.startMoment)) && (!last || !calendarUtil.isOverlapsTime(last.startMoment, last.endMoment, item.startMoment, item.endMoment))) {
                this.dataLevelMap.set(item.id, { level, data: item });
                last = item;
                return true;
            }
        });
    }

    /**
     * 点击
     *
     * @protected
     * @param {MouseEvent} e
     * @memberof IBzWeekDay
     */
    protected onClick(e: MouseEvent): void {
        const data: any = {};
        const minutes = Math.floor(e.offsetY / this.$c.layoutConfig.dragOffset) * this.$c.option.dragOffsetMinutes;
        const m = this.$c.calcDayStartDate(this.date);
        data[this.$c.option.startTimeKey] = m.add(minutes, 'minutes').format(this.$c.option.dateFormat);
        data[this.$c.option.endTimeKey] = m.add(this.$c.option.dragOffsetMinutes, 'minutes').format(this.$c.option.dateFormat);
        this.$c.externalNewItem(data);
    }

    /**
     * 「天」绘制
     *
     * @returns {VNode}
     * @memberof IBzWeekDay
     */
    public render(): VNode {
        return (
            <Host class='ibz-week-day' onClick={(e) => this.onClick(e)}>
                <div class='ibz-week-day-items'>
                    {this.items.map((item) => {
                        const self = this.dataLevelMap.get(item.id);
                        return <ibz-week-day-item key={item.id} $c={this.$c} todayStart={this.todayStart} todayEnd={this.todayEnd} date={this.date} item={item} level={self ? self.level : 0} index={this.index} width={this.el.offsetWidth} isParentHover={this.isHover} />;
                    })}
                </div>
            </Host>
        );
    }

}